// 导入Cocos Creator中必要的装饰器、组件基类以及节点类
import { _decorator, Component, instantiate, Node, Prefab, random, Sprite, SpriteFrame, UITransform, Vec2, view, EventTouch, sp, Button, Label } from 'cc';
//sp是spine动画
import { GlobalSettings } from './GlobalSettings';
import { GameData } from './GameData';
// 解构出装饰器相关的属性，用于定义类和属性等
const { ccclass, property } = _decorator;

import { config, GameState } from './Config';
import { Music } from './Music';

// 使用@ccclass装饰器将Game类标记为Cocos Creator可识别的组件类，参数'Game'是该类在编辑器中的名称
@ccclass('Game')
export class Game extends Component {

    // 定义一个数字类型的变量，用于存储分数,无法直接和label类型绑定，
    @property
    private score: number = 0;

    // 定义一个 Label 组件，用于显示分数
    @property(Label)
    private scoreLabel: Label = null;

    //setting设置按钮
    @property(Button)
    settingButton: Button = null;

    //设置面板
    @property(Node)
    settingPanel: Node = null;

    //设置面板-取消按钮
    @property(Button)
    cancleSsettingButton: Button = null;

    //3个控制
    //music控制
    @property(Button)
    musicButton: Button = null;

    @property(Button)
    musicButton1: Button = null;

    //音效
    @property(Button)
    shengyinButton: Button = null;

    @property(Button)
    shengyinButton1: Button = null;

    //震动
    @property(Button)
    zhendongButton: Button = null;

    @property(Button)
    zhendongButton1: Button = null;

    //全局变量，设置对象
    private globalSettings: GlobalSettings;

    //声音，音效，震动  的标记
    // private isMusicEnabled: boolean = true; 
    // private isShengyinEnabled: boolean = true;
    // private isZhendongEnabled: boolean = true;

    //游戏数据
    private gameData: GameData;

    // 用于存储Spine动画组件（这里按照你想用的sp.Skeleton类型），初始化为null
    @property(sp.Skeleton)
    animcom: sp.Skeleton = null;

    @property(sp.Skeleton)
    animcom1: sp.Skeleton = null;

    //drop之后的动画
    @property(sp.Skeleton)
    animcom2: sp.Skeleton = null;

    /**
     * 开始游戏面板
     * 通过@property装饰器将startPanel声明为一个属性，其类型为Node（代表游戏场景中的节点），
     * 并且在编辑器中可以将相应的节点拖曳赋值给它，初始值设置为null，
     * 这里它将用于关联表示开始游戏面板的节点，方便后续操作该面板的显示隐藏等状态。
     */
    @property(Node)
    startPanel: Node = null;

    /**
     * 暂停游戏面板
     */
    @property(Node)
    pausePanel: Node = null;

    /**
     * 结束游戏面板
     */
    @property(Node)
    endPanel: Node = null;

    /**
     * 游戏地图节点，要获取地图的宽高，500*900
     */
    @property(Node)
    gameMap: Node = null


    /**
     * 游戏背景节点
     */
    @property(Node)
    gameBg: Node = null

    /**
     * 方块预制体
     */
    @property(Prefab)
    blockPrefab: Prefab = null

    /**
    * blast动画预制体
    */
    @property(Prefab)
    blastPrefab: Prefab = null

    /**
     * 方块图片集
     */
    @property(SpriteFrame)
    blockSpriteFrame: SpriteFrame[] = []

    // 假设这里有一个属性用于存储动画组件，方便后续操作
    // @property(Animation)
    // blastAnimation: Animation = null;

    /**
     * 网格节点（二维数组）
     */
    gridNode: Node[][] = []

    /**
     * 网格数据（二维数组）
     */
    gridData: number[][] = []

    /**
     * 形状数据结构
     */
    shapeData = {
        id: 0, //形状ID  ，0-6
        startPos: new Vec2, //形状中心反馈的起始位置
        angleIndex: 0, //形状角度索引， 0-3
    }

    /**
     * 游戏状态
     */
    gameState: GameState = GameState.WAIT

    /**
     * 计时器
     */
    timer: number = 0

    /**
     * 音效组件
     */
    musicCom: Music = null


    //自己加的,代替onDown
    // 用于记录触摸开始的位置
    private startTouchPos: Vec2 = new Vec2(0, 0);
    // 用于判断滑动的距离阈值，可根据实际情况调整
    private swipeThreshold: number = 50;
    // 用于标记是否为向下滑动的布尔变量，初始化为false
    private isSwipeDown: boolean = false;
    // 新增：用于标记是否为向左滑动的布尔变量，初始化为false
    private isSwipeLeft: boolean = false;
    // 新增：用于标记是否为向右滑动的布尔变量，初始化为false
    private isSwipeRight: boolean = false;

    // 用于记录之前形状所占据的列的集合，初始化为空集合
    previousOccupiedColumns: Set<number> = new Set<number>();

    /**
      * 当前形状方块垂直下落的最终位置，初始化为null，后续根据方块情况更新
      */
    private finalDropPosition: Vec2 = null;

    // 用于存储最终下落位置对应的形状数据，包含形状ID、角度索引以及起始位置等信息
    finalShapeData = {
        id: 0, //形状ID  ，0-6
        startPos: new Vec2, //形状中心反馈的起始位置
        angleIndex: 0, //形状角度索引， 0-3
    }


    // 新增：用于记录需要下落方块位置的二维数组，每个元素形如 [row, col] 表示方块所在行和列
    blocksToDrop: number[][] = [];
    //消除行上面的方块实现缓慢下落
    // 新增：用于控制方块下落速度，单位可以是每秒下落多少格（这里假设每秒下落1格，可根据实际调整）
    private blockDropSpeed: number = 1;
    // 新增：用于记录每个需要下落的方块当前的下落进度（以格数为单位），初始化为0，使用二维数组对应记录blocksToDrop中的每个方块位置
    private blockDropPositions: number[][] = [];
    // 新增：用于标记是否正在进行方块下落动画，初始化为false
    private isBlocksDropping: boolean = false;


    // 新增：用于存储消除的行数的数组，比如 [6, 9, 10] 表示第6、9、10行被消除
    eliminatedRows: number[] = [];
    // 新增：用于存储每次消除行操作后更新的gridData数组状态
    updatedGridDataList: number[][] = [];


    //=================更新游戏分数==============================
    addScore(points: number) {
        this.score += points;
        this.updateScoreDisplay();
    }


    // 更新分数显示的方法
    private updateScoreDisplay() {
        if (this.scoreLabel) {
            this.scoreLabel.string = this.score.toString();
        }
    }

    //根据全局变量的值更新设置面板的：几个状态
    updateSettingPanelButtons() {
        // 根据 GlobalSettings 中的状态更新设置面板中的按钮显示
        this.musicButton.node.active = this.globalSettings.isMusicEnabled;
        this.musicButton1.node.active = !this.globalSettings.isMusicEnabled;
        this.shengyinButton.node.active = this.globalSettings.isSoundEnabled;
        this.shengyinButton1.node.active = !this.globalSettings.isSoundEnabled;
        this.zhendongButton.node.active = this.globalSettings.isVibrationEnabled;
        this.zhendongButton1.node.active = !this.globalSettings.isVibrationEnabled;
    }

    //点击设置打开设置面板
    onSettingButtonClick() {
        this.settingPanel.active = true;
        this.updateSettingPanelButtons();
    }

    onCancleSettingClick() {
        this.settingPanel.active = false;
    }

    onMusicButtonClick() {
        // 切换音乐状态
        this.globalSettings.isMusicEnabled = !this.globalSettings.isMusicEnabled;

        this.musicButton.node.active = false;
        this.musicButton1.node.active = true;

        if (this.globalSettings.isMusicEnabled) {
            console.log("音乐已开启");
            // 这里可以添加播放音乐的逻辑，例如：
            // this.audioSource.play();
        } else {
            console.log("音乐已关闭");
            // 这里可以添加暂停音乐的逻辑，例如：
            // this.audioSource.pause();
        }
    }

    onMusicButtonClick1() {
        // 切换音乐状态
        this.globalSettings.isMusicEnabled = !this.globalSettings.isMusicEnabled;

        this.musicButton.node.active = true;
        this.musicButton1.node.active = false;

        if (this.globalSettings.isMusicEnabled) {
            console.log("音乐已开启");
            // 这里可以添加播放音乐的逻辑，例如：
            // this.audioSource.play();
        } else {
            console.log("音乐已关闭");
            // 这里可以添加暂停音乐的逻辑，例如：
            // this.audioSource.pause();
        }
    }

    onShengyinButtonClick() {
        // 切换音效状态
        this.globalSettings.isSoundEnabled = !this.globalSettings.isSoundEnabled;

        this.shengyinButton.node.active = false;
        this.shengyinButton1.node.active = true;

        if (this.globalSettings.isSoundEnabled) {
            console.log("音效已开启");
            // 这里可以添加播放音效的逻辑，例如：
            // this.soundEffectSource.play();
        } else {
            console.log("音效已关闭");
            // 这里可以添加暂停音效的逻辑，例如：
            // this.soundEffectSource.pause();
        }
    }

    onShengyinButtonClick1() {
        // 切换音效状态
        this.globalSettings.isSoundEnabled = !this.globalSettings.isSoundEnabled;

        this.shengyinButton.node.active = true;
        this.shengyinButton1.node.active = false;

        if (this.globalSettings.isSoundEnabled) {
            console.log("音效已开启");
            // 这里可以添加播放音效的逻辑，例如：
            // this.soundEffectSource.play();
        } else {
            console.log("音效已关闭");
            // 这里可以添加暂停音效的逻辑，例如：
            // this.soundEffectSource.pause();
        }
    }

    onZhendongButtonClick() {
        // 切换震动状态
        this.globalSettings.isVibrationEnabled = !this.globalSettings.isVibrationEnabled;

        this.zhendongButton.node.active = false;
        this.zhendongButton1.node.active = true;

        if (this.globalSettings.isVibrationEnabled) {
            console.log("震动已开启");
            // 这里可以添加启动震动的逻辑，例如：
            // this.vibrateService.startVibration();
        } else {
            console.log("震动已关闭");
            // 这里可以添加停止震动的逻辑，例如：
            // this.vibrateService.stopVibration();
        }
    }

    onZhendongButtonClick1() {
        // 切换震动状态
        this.globalSettings.isVibrationEnabled = !this.globalSettings.isVibrationEnabled;

        this.zhendongButton.node.active = true;
        this.zhendongButton1.node.active = false;

        if (this.globalSettings.isVibrationEnabled) {
            console.log("震动已开启");
            // 这里可以添加启动震动的逻辑，例如：
            // this.vibrateService.startVibration();
        } else {
            console.log("震动已关闭");
            // 这里可以添加停止震动的逻辑，例如：
            // this.vibrateService.stopVibration();
        }
    }

    // 组件的start生命周期方法，在组件首次加载到场景中时被调用一次，常用于初始化相关操作
    start() {
        this.gameData = GameData.getInstance();

        this.globalSettings = GlobalSettings.getInstance(); //使用的还是前面index.ts中初始化的数据
        console.log("globalSettings.isVibrationEnabled:", this.globalSettings.isVibrationEnabled);//震动

        //点击设置打开设置面板,此时 开启音乐musicButton 按钮会显示出来
        this.settingButton.node.on('click', this.onSettingButtonClick, this);
        this.cancleSsettingButton.node.on('click', this.onCancleSettingClick, this);

        //点击切换是否开启音乐
        this.musicButton.node.on('click', this.onMusicButtonClick, this);
        this.musicButton1.node.on('click', this.onMusicButtonClick1, this);
        this.shengyinButton.node.on('click', this.onShengyinButtonClick, this);
        this.shengyinButton1.node.on('click', this.onShengyinButtonClick1, this);
        this.zhendongButton.node.on('click', this.onZhendongButtonClick, this);
        this.zhendongButton1.node.on('click', this.onZhendongButtonClick1, this);


        //blast帧动画播放没成功
        // if (this.blastPrefab) {
        //     let blastNode = instantiate(this.blastPrefab);
        //     // 添加到合适的父节点等操作（假设场景中有个gameLayer节点作为父节点）
        //     this.gameMap.addChild(blastNode);

        //     // 获取动画组件
        //     let animationComp = blastNode.getComponent(cc.Animation);
        //     if (animationComp) {
        //         // 假设动画剪辑资源名为blast.anim，加载动画剪辑
        //         cc.loader.loadRes('blast.anim', AnimationClip, (err, clip) => {
        //             if (err) {
        //                 console.error("加载动画剪辑出错: ", err);
        //                 return;
        //             }
        //             // 添加动画剪辑到动画组件并指定名称为blast
        //             animationComp.addClip(clip, 'blast');
        //         });
        //     }
        // }



        //在cocos中显示这个动画组件
        // 获取动画组件
        // 先通过节点路径或者其他查找方式获取到绑定了Spine动画的那个节点
        // 这里假设你给那个空节点命名为了 "spineNode"，可以根据节点名称来查找
        const spineNode: Node = this.node.getChildByName('spineNode');
        const spineNode1: Node = this.node.getChildByName('spineNode1');
        const spineNode2: Node = this.node.getChildByName('spineNode2');
        if (spineNode) {
            // 从该节点上获取Skeleton组件
            this.animcom = spineNode.getComponent(sp.Skeleton);
            this.animcom1 = spineNode1.getComponent(sp.Skeleton);

            //drop动画
            this.animcom2 = spineNode2.getComponent(sp.Skeleton);

            //动画的回调函数
            this.animcom.setCompleteListener(function () {
                console.log("动画的回调函数...");
            });


            if (this.animcom) {
                // 调用播放动画的方法，这里假设动画名称为 "idle"，你需要替换成实际Spine动画中存在的有效动画名称
                // this.playAnimation("idle");
                console.log("animation动画可以找到....")
                // this.animcom.setAnimation(0, "idle", true);

                //如果cocos中的spine动画的编辑器中：那个组件sp.Skeleton 那里的属性Animation选择具体动画则默认开始加载会播放一次，loop是循环播放
                // 暂停动画，防止自动播放
                // 暂停动画，将时间缩放比例设置为0，这样动画就会停止在当前帧
                // this.animcom.timeScale = 0;
            } else {
                console.warn("未在指定节点上找到Skeleton组件，请检查是否正确添加了组件");
            }
        } else {
            console.warn("未找到名为 'spineNode' 的节点，请检查节点名称和层级结构");
        }

        //初始化spine动画
        // var animcom: sp.Skeleton = this.node.getComponent(sp.Skeleton);
        // if (animcom) {
        //     // 设置要播放的动画名称，这里假设你的动画名称叫"animation"，你需要根据实际情况调整
        //     // animcom.setAnimation(0, "animation", true);

        //     //其中 0 表示动画轨道的索引，通常在只有一个主要动画轨道时使用 0。"idle" 是要播放的动画名称，
        //     // 这里假设在 Spine 动画数据中有一个名为 "idle" 的动画，比如可能是角色的空闲状态动画。true 表示动画是否循环播放，
        //     // 这里设置为 true 意味着动画会不断循环播放
        //     animcom.setAnimation(0, "idle", true); //记得给cocos中的sp.Skeleton 组件加上动画：Animation：idle 
        // } else {
        //     console.warn("未找到SkeletonAnimation组件，请检查节点是否添加了该组件");
        // }





        // 这里目前可以添加游戏开始时的一些初始化逻辑，比如初始化游戏数据、设置初始状态等，
        // 但当前示例中暂时为空，可根据具体游戏需求进行扩展。


        //自己加的
        //获取当前游戏场景所在的节点（或者能接收触摸事件的相关节点），这里假设是整个场景节点
        // 获取当前游戏场景所在的节点（通常是Canvas节点）
        // 先获取当前节点所在的场景节点
        //==========================================================================================
        const sceneNode = this.node.scene;
        if (sceneNode) {
            // 再从场景节点中查找名为'Canvas'的子节点
            const gameSceneNode = sceneNode.getChildByName('Canvas');
            if (gameSceneNode) {
                //    gameSceneNode.on(Node.EventType.TOUCH_END, this.onScreenClick, this);
                console.log('在场景中找到名为Canvas的节点');

                // 监听触摸开始事件
                gameSceneNode.on(Node.EventType.TOUCH_START, this.onTouchStart, this);
                // 监听触摸移动事件
                gameSceneNode.on(Node.EventType.TOUCH_MOVE, this.onTouchMove, this);
                // 监听触摸结束事件
                gameSceneNode.on(Node.EventType.TOUCH_END, this.onTouchEnd, this);
            } else {
                console.warn('在场景中未找到名为Canvas的节点');
            }
        } else {
            console.warn('当前节点未关联到有效的场景');
        }
        //==========================================================================================

        //初始化音效组件
        this.musicCom = this.node.getComponent(Music)

        //计算设计视图的宽高比
        let designRatio = view.getDesignResolutionSize().width / view.getDesignResolutionSize().height
        //背景节点使用屏幕的高度，保持背景的宽高比
        this.gameBg.getComponent(UITransform).setContentSize(view.getVisibleSize().height * designRatio, view.getVisibleSize().height)
    }

    //自己加的，关闭onUp()
    onScreenClick(event: EventTouch) {
        // 处理点击逻辑，例如调用相关方法改变游戏元素等
        this.changeShapeAngle();
    }

    //自己加的，关闭onDown()
    onTouchStart(event: EventTouch) {
        // 获取触摸开始的位置
        this.startTouchPos = event.getLocation();
        // console.log("startTouchPos:", this.startTouchPos)
    }

    onTouchMove(event: EventTouch) {
        const currentTouchPos = event.getLocation();
        const deltaY = currentTouchPos.y - this.startTouchPos.y;
        const deltaX = currentTouchPos.x - this.startTouchPos.x;

        //发现deltaY 是负值
        if (Math.abs(deltaY) > this.swipeThreshold) {
            this.isSwipeDown = true;
        } else {
            this.isSwipeDown = false;
        }
        // console.log(this.isSwipeDown)

        if (deltaX < -this.swipeThreshold) {
            this.isSwipeLeft = true;
        } else {
            this.isSwipeLeft = false;
        }

        if (deltaX > this.swipeThreshold) {
            this.isSwipeRight = true;
        } else {
            this.isSwipeRight = false;
        }
    }

    //将单击和下滑都整合到这一个逻辑中来
    //将单击和下滑都整合到这一个逻辑中来
    onTouchEnd(event: EventTouch) {
        if (this.isSwipeDown) {
            this.onDown();
            console.log("down");
        } else if (this.isSwipeLeft) {
            // 计算滑动距离绝对值，用于判断移动格数
            const swipeDistanceX = Math.abs(event.getLocation().x - this.startTouchPos.x);
            // 定义不同距离区间对应的移动格数，这里示例设置两个区间，可以根据实际需求调整
            let moveSteps = 1; // 默认移动1格
            if (swipeDistanceX > 100 && swipeDistanceX <= 200) {
                moveSteps = 2; // 滑动距离在100 - 200之间移动2格
            } else if (swipeDistanceX > 200) {
                moveSteps = 3; // 滑动距离大于200移动3格
            }
            for (let i = 0; i < moveSteps; i++) {
                this.onLeft();
            }
        } else if (this.isSwipeRight) {
            const swipeDistanceX = Math.abs(event.getLocation().x - this.startTouchPos.x);
            let moveSteps = 1;
            if (swipeDistanceX > 100 && swipeDistanceX <= 200) {
                moveSteps = 2;
            } else if (swipeDistanceX > 200) {
                moveSteps = 3;
            }
            for (let i = 0; i < moveSteps; i++) {
                this.onRight();
            }
        } else {
            //判断是否是单击
            //  const touchDuration = event.getDelta().mag() / 60; 
            //  if (touchDuration < 0.3 && event.getDelta().mag() < 10) {
            //      this.onScreenClick(event);
            //  }
            this.onUp();
        }

        // 重置所有滑动标记变量
        this.isSwipeDown = false;
        this.isSwipeLeft = false;
        this.isSwipeRight = false;
    }
    //=============================================================================



    /**
     * 形状自动下落
     */
    // autoDown() {
    //     //如果没有创建形状，则返回
    //     if (this.shapeData.id === 0) return
    //     //如果不在游戏中，则直接返回
    //     if (this.gameState !== GameState.PLAYING) return

    //     //先清除形状
    //     this.clearShape()
    //     //再形状的行加一
    //     this.shapeData.startPos.x += 1;

    //     if (this.checkShape()) {
    //         //播放移动音效
    //         this.musicCom.playMoverAudio()
    //         //渲染形状
    //         this.renderShape();

    //         // this.renderFinalDropShape();//渲染底部提示形状
    //     } else {
    //         this.shapeData.startPos.x -= 1;//还原到原来的位置
    //         this.renderShape() //渲染形状
    //         this.checkRows() //停止下落了，检查下是否有行满了
    //         this.createShape()  //重新生成形状
    //     }

    // }


    // 震动功能的实现
    /**
     * 触发设备震动功能。
     * 如果音乐功能启用，则调用抖音小游戏的 tt.vibrateShort 方法来实现短震动。
     * 成功或失败时，会在控制台打印相应信息。
     */
    vibrate() {
        //是否关闭震动
        if (this.isMusicEnabled) {
            // 使用抖音小游戏的 tt.vibrateShort 方法实现震动
            tt.vibrateShort({
                success(res) {
                    console.log("抖音震动")
                    console.log(`${res}`);
                },
                fail(res) {
                    console.log(`vibrateShort 调用失败: ${res}`);
                }
            });
        }

    }

    //加了放置播放音效
    autoDown() {
        // 如果游戏已经结束，直接返回，不再进行形状下落相关操作以及播放下落放置音效
        if (this.gameState === GameState.OVER) return;

        // 如果没有创建形状，则返回
        if (this.shapeData.id === 0) return
        // 如果不在游戏中，则直接返回
        if (this.gameState !== GameState.PLAYING) return

        // 记录上一帧形状的垂直位置（x坐标代表垂直位置，这里基于你代码中的设定）
        const previousYPos = this.shapeData.startPos.x;

        // 先清除形状
        this.clearShape();
        // 再形状的行加一
        this.shapeData.startPos.x += 1;

        if (this.checkShape()) {
            // 渲染形状
            this.renderShape();

            // this.renderFinalDropShape();//渲染底部提示形状
        } else {
            this.shapeData.startPos.x -= 1;//还原到原来的位置
            this.renderShape() //渲染形状

            // 判断当前位置（还原后的位置）和上一帧位置是否相同，如果相同说明没有移动，也就是到达底部了
            if (this.shapeData.startPos.x === previousYPos) {
                console.log("到达底部了...")
                this.musicCom.playMoverAudio();

                // 到达底部播放动画
                if (this.animcom2) {
                    const animationNode = this.animcom2.node;
                    const uiTransform = animationNode.getComponent(UITransform);

                    let mapWidth = this.gameMap.getComponent(UITransform).width;
                    let mapHeight = this.gameMap.getComponent(UITransform).height;
                    if (uiTransform) {
                        // 根据方块的位置设置动画节点的位置
                        // 根据方块的位置设置动画节点的位置
                        let x = -mapWidth / 2 + config.blockWidth / 2 + this.shapeData.startPos.y * config.blockWidth;
                        let y = mapHeight / 2 - config.blockHeight / 2 - this.shapeData.startPos.x * config.blockHeight;
                        animationNode.setPosition(x, y);
                        this.animcom2.setAnimation(0, "animation1", false);
                        console.log("执行掉落动画animcom2...");
                    }
                }

                // 到达底部时调用震动功能
                this.vibrate();
            }

            this.checkRows() //停止下落了，检查下是否有行满了
            this.createShape()  //重新生成形状
        }
    }


    //大概16毫秒就触发一次   更新只控制下落，1秒下落一次
    //在游戏开发中，deltaTime 通常表示自上一帧到当前帧经过的时间，单位是秒。其具体数值取决于游戏的帧率和硬件性能等因素，不是一个固定的值。
    // 组件的update生命周期方法，会在每一帧被调用，用于处理游戏中每帧需要更新的逻辑，例如物体的移动、状态变化等
    // update(deltaTime: number) {

    //     // deltaTime参数表示自上一帧到当前帧经过的时间，单位是秒，可以基于这个时间间隔来实现平滑的动画效果或其他实时更新逻辑，
    //     // 同样，当前示例中暂时没有具体的更新逻辑，可后续根据游戏功能需求添加相应代码。

    //     //加上时间
    //     this.timer += deltaTime
    //     //如果超过1秒，则触发形状自动下落
    //     if (this.timer > 1) {
    //         this.timer = 0  //计时器归零
    //         this.autoDown()
    //     }
    // }

    update(deltaTime: number) {
        // deltaTime参数表示自上一帧到当前帧经过的时间，单位是秒，可以基于这个时间间隔来实现平滑的动画效果或其他实时更新逻辑

        if (this.isBlocksDropping) {
            // 遍历每个需要下落的方块，更新它们的位置
            for (let i = 0; i < this.blockDropPositions.length; i++) {
                let [row, col] = this.blockDropPositions[i];
                // 根据下落速度和经过的时间，更新当前方块的下落进度
                this.blockDropPositions[i][0] += this.blockDropSpeed * deltaTime;
                // 向下取整获取当前方块已经下落完整的格数
                let droppedRows = Math.floor(this.blockDropPositions[i][0]);
                if (droppedRows > 0) {
                    // 更新方块在网格数据中的位置，将其下移相应格数
                    this.gridData[row + droppedRows][col] = this.gridData[row][col];
                    this.gridData[row][col] = 0;
                    // 更新当前方块的位置信息为新的下落位置
                    this.blockDropPositions[i][0] -= droppedRows;
                    this.blockDropPositions[i][1] = col;
                }
            }

            // 检查所有方块是否都已经完成下落
            let allBlocksDropped = true;
            for (let pos of this.blockDropPositions) {
                if (pos[0] > 0) {
                    allBlocksDropped = false;
                    break;
                }
            }

            if (allBlocksDropped) {
                // 如果所有方块都已完成下落，标记下落动画结束
                this.isBlocksDropping = false;
                // 重新渲染网格，显示最终的下落效果
                this.renderGrid();
            }
        }

        // 原有的游戏逻辑代码，如形状自动下落相关逻辑等，保持不变
        this.timer += deltaTime;
        if (this.timer > 1) {
            this.timer = 0;
            this.autoDown();
        }
    }



    /**
     * 开始游戏
     * 此方法用于处理游戏开始的相关操作，在本示例中主要功能是隐藏开始游戏面板，
     * 通过将startPanel节点的active属性设置为false，使得该节点及其所有子节点在游戏场景中不可见，
     * 相当于关闭了开始游戏面板，标志着游戏正式开始进入实际游戏过程。
     */
    //最开始点击按钮“开始游戏”就调用这个方法
    gameStart() {
        console.log("gameStart..........")
        //隐藏开始面板
        this.startPanel.active = false;
        this.endPanel.active = false;//隐藏结束面板
        this.gameState = GameState.PLAYING

        this.initGridData(); //初始化网格数据，先全部都是0，二维数组gridData
        this.initGridNode(); //初始化网格节点，二维数组gridNode,元素全是方块预制体，根据上面的gridData的0,1确定是否贴图，1就贴图
        //到这里，2个数组都初始化了，gridData都是0，类型是number;  另一个数组gridNode,元素全部填充为方块预制体，每个预制体有坐标和背景图，有的背景图为空有点贴图
        //也给地图渲染了row*col 个节点，把预制体背景图去掉，然后改成用动态的图片集附上去

        this.renderGrid();//渲染网格，给数组gridNode的元素预制体方块贴图
        this.createShape();  //创建形状（7种）
    }

    /** 
     * 游戏暂停
     */
    gamePause() {
        this.pausePanel.active = true
        this.gameState = GameState.PAUSE
    }

    /**
     * 游戏继续
     */
    gameContinue() {
        this.pausePanel.active = false
        this.gameState = GameState.PLAYING
    }

    /**
     * 游戏结束
     */
    gameOver() {
        this.endPanel.active = true
        this.gameState = GameState.OVER
    }

    //初始化底部方块
    initLayout() {
        for (let row = 0; row < config.row; row++) {
            if (row == 4 || row == 5) {
                this.gridData[row][0] = 1;
                this.gridData[row][9] = 1;
            }
            if (row == 6 || row == 7) {
                this.gridData[row][0] = 1;
                this.gridData[row][1] = 1;
                this.gridData[row][config.col - 1] = 1;
                this.gridData[row][config.col - 2] = 1;
            }
            if (row == 8 || row == 9) {
                this.gridData[row][0] = 1;
                this.gridData[row][1] = 1;
                this.gridData[row][2] = 1;
                this.gridData[row][3] = 1;
                this.gridData[row][config.col - 1] = 1;
                this.gridData[row][config.col - 2] = 1;
                this.gridData[row][config.col - 3] = 1;
                this.gridData[row][config.col - 4] = 1;
            }
            if (row == 10 || row == 11 || row == 12) {
                this.gridData[row][0] = 1;
                this.gridData[row][1] = 1;
                this.gridData[row][2] = 1;
                this.gridData[row][3] = 1;
                this.gridData[row][config.col - 1] = 1;
                this.gridData[row][config.col - 2] = 1;
                this.gridData[row][config.col - 3] = 1;
                this.gridData[row][config.col - 4] = 1;
            }
            if (row == 13) {
                this.gridData[row][0] = 1;
                this.gridData[row][1] = 1;
                this.gridData[row][2] = 1;
                this.gridData[row][3] = 1;
                this.gridData[row][5] = 1;
                this.gridData[row][6] = 1;
                this.gridData[row][7] = 1;
                this.gridData[row][8] = 1;
                this.gridData[row][9] = 1;
            }
            if (row == 14) {
                this.gridData[row][0] = 1;
                this.gridData[row][1] = 1;
                this.gridData[row][2] = 1;
                this.gridData[row][3] = 1;
                this.gridData[row][4] = 1;
                this.gridData[row][6] = 1;
                this.gridData[row][7] = 1;
                this.gridData[row][8] = 1;
                this.gridData[row][9] = 1;
            }
            if (row == 15) {
                this.gridData[row][0] = 1;
                this.gridData[row][1] = 1;
                this.gridData[row][2] = 1;
                this.gridData[row][3] = 1;
                this.gridData[row][4] = 1;
                this.gridData[row][5] = 1;
                this.gridData[row][7] = 1;
                this.gridData[row][8] = 1;
                this.gridData[row][9] = 1;
            }
            if (row == 16 || row == 17) {
                this.gridData[row][0] = 1;
                this.gridData[row][1] = 1;
                this.gridData[row][2] = 1;
                this.gridData[row][3] = 1;
                this.gridData[row][4] = 1;
                this.gridData[row][config.col - 1] = 1;
                this.gridData[row][config.col - 2] = 1;
                this.gridData[row][config.col - 3] = 1;
                this.gridData[row][config.col - 5] = 1;
            }
            // for (let col = 0; col < config.col; col++) {

            // }
        }

    }

    /** 
     * 初始化网格节点 gridNode
     */
    initGridNode() {
        //避免游戏结束后，网格重复渲染  避免预制体重新渲染，但是初始化布局节点得重新写
        if (this.gridNode.length) {
            this.initLayout();//生成下面的方块
            return
        }

        // 获取地图的宽度和高度
        let mapWidth = this.gameMap.getComponent(UITransform).width;
        let mapHeight = this.gameMap.getComponent(UITransform).height;

        for (let row = 0; row < config.row; row++) {
            //这行代码会初始化 this.gridNode 二维数组中当前行对应的内层数组，用于存放该行的方块节点，确保每一行都有一个对应的空数组来存储列方向的方块节点信息。
            this.gridNode[row] = []; // 初始化行数据 ,创建18个一维数组


            for (let col = 0; col < config.col; col++) {
                let blockNode = instantiate(this.blockPrefab); //这个方块预制体已经没有背景图了

                //设置方块的宽度和高度，都是50，也可以在预制体的block那里设置
                blockNode.getComponent(UITransform).setContentSize(config.blockWidth, config.blockHeight)

                // 方块x位置 = 地图左边x坐标，加上方块宽度的一半，再加上当前列乘以方块宽度
                let x = -mapWidth / 2 + config.blockWidth / 2 + col * config.blockWidth; //方块中心
                // 方块y位置 = 地图顶部y坐标，减去方块高度的一半，再减去当前行乘以方块高度
                let y = mapHeight / 2 - config.blockHeight / 2 - row * config.blockHeight;

                // 设置方块的位置
                blockNode.setPosition(x, y);  //(-225, 425)  -250+25  450-23
                blockNode.getComponent(Sprite).spriteFrame = this.blockSpriteFrame[1];

                // 将方块加到游戏地图
                this.gameMap.addChild(blockNode);
                //执行到这里，游戏地图已经可以显示全部的方块了，后面需要把方块预制体的背景图去掉，改成动态的图片集

                // 保存到方块节点数组
                this.gridNode[row][col] = blockNode;   //这时，方块都产生了，但是方块没贴图
            }
        }

        //这就可以实现方块下移动
        // let blockNode = instantiate(this.blockPrefab);
        // blockNode.setPosition(-225, 400);
        // blockNode.getComponent(Sprite).spriteFrame = this.blockSpriteFrame[2];
        // this.gameMap.addChild(blockNode);

        this.initLayout();
    }



    /**
     * 初始化网格数据 给二维数组元素全部复制为0
     */
    initGridData() {
        this.gridData = new Array(config.row).fill(0).map(
            () => new Array(config.col).fill(0)
        )

        //打印看看,二维数组，18行10列全部都是0
        // console.log("gridData:");
        // for (let row = 0; row < this.gridData.length; row++) {
        //     let rowStr = "";
        //     for (let col = 0; col < this.gridData[row].length; col++) {
        //         rowStr += this.gridData[row][col] + " ";
        //     }
        //     console.log(rowStr);  // 这里每行元素打印后会自动换行，因为每次console.log都会另起一行输出
        // }
    }

    /**
     * 创建形状（随机7种）
     */
    createShape() {
        //消除最终形成形状
        // this.clearFinalDropShape();

        //随机生成形状ID，1-7代表不同的形状
        //Math.random() 函数会生成一个大于等于 0 且小于 1 的随机小数 [0,1)   
        //floor  向下取整
        this.shapeData.id = Math.floor(Math.random() * 7) + 1 //[0,1)   [1,8)  1-7
        //随机生成形状角度索引，0-3代表不同的形状角度
        this.shapeData.angleIndex = Math.floor(Math.random() * 4)  //[0,4)  0,1,2,3
        //设置形状中心方块的起始位置
        this.shapeData.startPos = new Vec2(1, 5)

        //判断游戏是否结束
        // 判断游戏是否结束
        if (!this.checkShape()) {
            this.musicCom.playGameOverAudio(); // 播放游戏结束音效
            this.gameOver(); // 游戏结束
        } else {
            // 形状合法，播放移动音效
            // this.musicCom.playMoverAudio();
        }

        // 渲染形状前，计算并存储垂直下落最终位置
        this.finalDropPosition = this.findFinalDropPosition();
        // console.log("方块预计下落位置；", this.finalDropPosition)
        // 保存最终下落位置对应的形状数据
        this.finalShapeData = {
            id: this.shapeData.id,
            angleIndex: this.shapeData.angleIndex,
            startPos: this.finalDropPosition
        };

        //渲染形状
        this.renderShape()

        // 渲染最终下落位置对应的形状（换个贴图来区分显示最终下落位置）
        // this.renderFinalDropShape();

    }

    //用于计算形状垂直下落最终位置的核心逻辑
    findFinalDropPosition(): Vec2 {
        let currentPos = this.shapeData.startPos.clone();
        let shapeConfig = config[`shape${this.shapeData.id}`][this.shapeData.angleIndex];
        while (true) {
            let canDrop = true;
            currentPos.x += 1;
            shapeConfig.forEach(blockPos => {
                let row = currentPos.x + blockPos.x;
                let col = currentPos.y + blockPos.y;
                // 判断形状中的每个方块是否超出网格范围或者已有方块占据（即不能下落）
                if (row < 0 || row >= config.row || col < 0 || col >= config.col || this.gridData[row][col] !== 0) {
                    canDrop = false;
                }
            });
            if (!canDrop) {
                currentPos.x -= 1;
                break;
            }
        }
        return currentPos;
    }


    /**
     * 渲染形状
     */
    // 新增方法，用于清除指定列的灰色方块
    clearColumnGrayBlocks(columns: Set<number>) {
        columns.forEach(column => {
            for (let row = 0; row < config.row; row++) {
                if (this.gridData[row][column] === 0) {
                    this.gridNode[row][column].getComponent(Sprite).spriteFrame = null;
                }
            }
        });
    }

    renderShape() {
        // 用于记录形状方块当前所占据的列的集合
        let currentOccupiedColumns: Set<number> = new Set();
        // 获取当前形状配置
        let shapeConfig = config[`shape${this.shapeData.id}`][this.shapeData.angleIndex];
        shapeConfig.forEach(blockPos => {
            // 计算方块在网格中的位置
            let row = this.shapeData.startPos.x + blockPos.x;
            let col = this.shapeData.startPos.y + blockPos.y;
            // 记录形状ID
            this.gridData[row][col] = this.shapeData.id;
            // 设置方块的图片
            this.gridNode[row][col].getComponent(Sprite).spriteFrame = this.blockSpriteFrame[this.shapeData.id - 1];
            // 将当前方块所在的列添加到集合中
            currentOccupiedColumns.add(col);
        });

        // 先清除之前形状所在列的灰色方块（如果有）
        if (this.previousOccupiedColumns) {
            this.clearColumnGrayBlocks(this.previousOccupiedColumns);
        }

        // 遍历形状方块所占据的列，填充空白位置为灰色贴图
        currentOccupiedColumns.forEach(column => {
            for (let row = 0; row < config.row; row++) {
                if (this.gridData[row][column] === 0) {
                    this.gridNode[row][column].getComponent(Sprite).spriteFrame = this.blockSpriteFrame[7]  //灰色图片
                }
            }
        });

        // 更新之前形状所占据的列的记录
        this.previousOccupiedColumns = currentOccupiedColumns;

        // console.log("renderShape()================================");
        // for (let row = 0; row < this.gridData.length; row++) {
        //     let rowStr = "";
        //     for (let col = 0; col < this.gridData[row].length; col++) {
        //         rowStr += this.gridData[row][col] + " ";
        //     }
        //     console.log(rowStr);
        // }

        // 打印形状方块所占据的列信息
        // console.log("形状方块所占据的列：", Array.from(currentOccupiedColumns).sort((a, b) => a - b));
    }

    //渲染最终形成位置
    renderFinalDropShape() {
        // 获取当前形状配置
        let shapeConfig = config[`shape${this.finalShapeData.id}`][this.finalShapeData.angleIndex];
        shapeConfig.forEach(blockPos => {
            // 计算方块在网格中的位置
            let row = this.finalShapeData.startPos.x + blockPos.x;
            let col = this.finalShapeData.startPos.y + blockPos.y;
            // 记录形状ID
            // this.gridData[row][col] = this.finalShapeData.id;
            // 设置方块的图片
            this.gridNode[row][col].getComponent(Sprite).spriteFrame = this.blockSpriteFrame[8];//垂直下落的最终位置
        });

        // // 先清除之前形状所在列的灰色方块（如果有）
        // if (this.previousOccupiedColumns) {
        //     this.clearColumnGrayBlocks(this.previousOccupiedColumns);
        // }
    }

    //消除最终形成位置
    clearFinalDropShape() {
        if (this.finalShapeData) {
            return
        }

        // 获取当前形状配置
        let shapeConfig = config[`shape${this.finalShapeData.id}`][this.finalShapeData.angleIndex];
        shapeConfig.forEach(blockPos => {
            // 计算方块在网格中的位置
            let row = this.finalShapeData.startPos.x + blockPos.x;
            let col = this.finalShapeData.startPos.y + blockPos.y;
            // 记录形状ID
            this.gridData[row][col] = this.finalShapeData.id;
            // 设置方块的图片
            this.gridNode[row][col].getComponent(Sprite).spriteFrame = null;//垂直下落的最终位置
        });

        // 先清除之前形状所在列的灰色方块（如果有）
        if (this.previousOccupiedColumns) {
            this.clearColumnGrayBlocks(this.previousOccupiedColumns);
        }
    }

    /**
     * 渲染网格
     */
    renderGrid() {
        // 单独遍历打印gridData数组
        // console.log("renderGrid()方法中,单独打印gridData数组内容如下：");
        // for (let row = 0; row < config.row; row++) {
        //     let rowStr = "";
        //     for (let col = 0; col < config.col; col++) {
        //         rowStr += this.gridData[row][col] + " ";
        //     }
        //     console.log(rowStr);
        // }
        //前14行都是0，后4行有1有0,0就为空，非0就有方块

        for (let row = 0; row < config.row; row++) {
            for (let col = 0; col < config.col; col++) {
                // 形状ID
                let shapeId = this.gridData[row][col]   //gridData数据是0或者1-6
                //方块图片
                this.gridNode[row][col].getComponent(Sprite).spriteFrame = this.blockSpriteFrame[shapeId - 1]  //-1是空的图 或者0-6,贴不同的图
            }
        }
    }

    /**
     * 清除形状  避免清除底部的提示形状
     */
    clearShape() {
        // 获取当前实际形状配置
        let shapeConfig = config[`shape${this.shapeData.id}`][this.shapeData.angleIndex];
        shapeConfig.forEach(blockPos => {
            // 计算实际形状方块在网格中的位置
            let row = this.shapeData.startPos.x + blockPos.x;
            let col = this.shapeData.startPos.y + blockPos.y;
            // 记录形状ID（这里是实际形状的相关操作）
            this.gridData[row][col] = 0;
            // 设置方块的图片（清除实际形状方块的图片显示）
            this.gridNode[row][col].getComponent(Sprite).spriteFrame = null;
        });
    }

    /**
     * 检查形状
     */
    checkShape() {
        //获取当前形状配置
        let shapeConfig = config[`shape${this.shapeData.id}`][this.shapeData.angleIndex]
        //遍历形状的每个方块
        for (let i = 0; i < shapeConfig.length; i++) {
            //计算方块的行和列
            let row = this.shapeData.startPos.x + shapeConfig[i].x
            let col = this.shapeData.startPos.y + shapeConfig[i].y
            // 判断行是否超出网格范围
            if (row < 0 || row >= config.row) {
                return false
            }
            //判断列是否超出网格范围
            if (col < 0 || col >= config.col) {
                return false
            }
            //当前节点是否有形状
            if (this.gridData[row][col] !== 0) {
                return false
            }
        }
        return true
    }

    //自己新增的渲染方法，原始的渲染方法只能渲染贴图，不能更改预制体的位置
    renderGridNew(eliminatedRows: number[], updatedGridDataList: number[][][]) {

        for (let row = 0; row < config.row; row++) {
            for (let col = 0; col < config.col; col++) {
                // 形状ID
                let shapeId = this.updatedGridDataList[row][col]   //gridData数据是0或者1-6
                //方块图片
                this.gridNode[row][col].getComponent(Sprite).spriteFrame = this.blockSpriteFrame[shapeId - 1]  //-1是空的图 或者0-6,贴不同的图
            }
        }


        // 获取地图的宽度和高度
        let mapWidth = this.gameMap.getComponent(UITransform).width;
        let mapHeight = this.gameMap.getComponent(UITransform).height;

        // 遍历每一行
        // for (let row = 0; row < config.row; row++) {
        //     // 遍历每一列
        //     for (let col = 0; col < config.col; col++) {
        //         // 判断当前行是否是需要消除的行
        //         // let isEliminatedRow = eliminatedRows.includes(row); //包含方法爆红
        //         let isEliminatedRow = this.eliminatedRows.some((eliminatedRow) => eliminatedRow === row);
        //         if (isEliminatedRow) {
        //             // 如果是消除行，先隐藏对应的预制体（可以根据实际需求决定是否隐藏，这里简单示例隐藏处理）
        //             this.gridNode[row][col].active = false;
        //             continue;
        //         }

        //         // 获取当前方块预制体对应的节点
        //         let blockNode = this.gridNode[row][col];

        //         // 根据是否有更新后的gridData数据来确定新的方块显示情况（比如是否有新的方块出现等）
        //         let newGridDataState = updatedGridDataList.find((gridDataState, index) => {
        //             return gridDataState[row][col] !== 0;
        //         });

        //         if (newGridDataState) {
        //             // 如果有对应的新数据，说明方块状态有变化，设置新的贴图（这里假设根据非0值来设置贴图，具体逻辑可按实际需求调整）
        //             blockNode.getComponent(Sprite).spriteFrame = this.blockSpriteFrame[1];
        //         }

        //         // 计算方块新的y位置，实现往下移动效果（这里简单示例为直接下移一定距离，可以根据消除的行数等更精确计算）
        //         let newY = mapHeight / 2 - config.blockHeight / 2 - row * config.blockHeight;
        //         // 如果当前行在已消除行的下面，需要额外下移一定距离，模拟方块下落效果
        //         let rowsBelowEliminated = eliminatedRows.filter(r => r < row).length;
        //         newY -= rowsBelowEliminated * config.blockHeight;

        //         // 设置方块的新位置
        //         blockNode.setPosition(
        //             -mapWidth / 2 + config.blockWidth / 2 + col * config.blockWidth,
        //             newY
        //         );

        //         // 设置方块为可见状态（如果之前有隐藏等操作）
        //         blockNode.active = true;
        //     }
        // }

        // 可以在这里添加其他与重新渲染相关的逻辑，比如更新动画状态等
    }

    /**
     * 检查行是否满了
     */
    checkRows() {
        //eliminatedRows 用来村消除行12,13，  updatedGridDataList存12,13行为0的gridData数组
        // 初始化eliminatedRows数组，用于记录满行的行号
        // this.eliminatedRows = [];
        // // 初始化updatedGridDataList数组，用于存储每次判断满行操作后对应的gridData状态（满行置0后的状态），这里只需要二维数组即可
        // this.updatedGridDataList = [];

        // // 遍历每一行，检查是否满行
        // for (let row = 0; row < config.row; row++) {
        //     let isFull = true;
        //     // 遍历当前行的每一列，检查是否有空的格子
        //     for (let col = 0; col < config.col; col++) {
        //         if (this.gridData[row][col] === 0) {
        //             isFull = false;
        //             break;
        //         }
        //     }
        //     if (isFull) {
        //         // 如果当前行是满行，先将行号添加到eliminatedRows数组
        //         this.eliminatedRows.push(row);
        //         // 复制当前的gridData状态，用于后续操作（保持原始状态不被修改）
        //         let currentGridData = this.gridData.map(r => r.slice());
        //         // 将满行对应的所有列元素置为0
        //         currentGridData[row] = new Array(config.col).fill(0);
        //         // 将处理后的currentGridData添加到updatedGridDataList数组，此时它就是二维数组了
        //         this.updatedGridDataList.push(currentGridData);
        //     }
        // }

        // // 打印eliminatedRows数组
        // console.log("消除的行数（eliminatedRows）:", this.eliminatedRows);
        // // 打印updatedGridDataList数组内容
        // console.log("更新后的gridData数组状态列表（updatedGridDataList）:");
        // this.updatedGridDataList.forEach((gridDataState, index) => {
        //     console.log(`第${index + 1}次判断满行操作后的gridData状态:`);
        //     gridDataState.forEach(row => {
        //         console.log(row.join(" "));
        //     });
        // });

        //============================接下来写个渲染的方法，渲染这个updatedGridDataList ==========
        // 判断eliminatedRows数组不为空时，调用renderGridNew进行重新渲染
        // if (this.eliminatedRows.length > 0) {//需要消除动画
        //     this.renderGridNew(this.eliminatedRows, this.updatedGridDataList);
        // }

        //======================================================================================


        // 获取最后一行的索引，因为索引从0开始，所以要减1
        let lastRow = config.row - 1;
        // 是否需要渲染，初始化为 false，表示默认不需要渲染
        let isRender = false;
        let eliminatedLines = 0;  //用来服务记录游戏分数新增的

        while (lastRow >= 0) {
            // 假设当前行是满行，后续通过循环检查来验证
            let isFull = true;

            // 遍历当前行的每一列，检查是否有空的格子
            for (let col = 0; col < config.col; col++) {
                if (this.gridData[lastRow][col] === 0) {
                    isFull = false;
                    break;
                }
            }

            if (isFull) {
                isRender = true;

                eliminatedLines++;
                // 如果当前行满了，将上面的行数据依次下移一行
                for (let r = lastRow; r > 0; r--) {
                    for (let c = 0; c < config.col; c++) {
                        this.gridData[r][c] = this.gridData[r - 1][c];
                        // 新增：如果上方的方块原来所在位置不是最顶部（r > 0），则记录其需要下落，将其位置信息添加到数组中
                        if (r > 0) {
                            this.blocksToDrop.push([r, c]);//他是记录了需要下落方块的所在位置坐标：比如13,0  第14行1列
                        }
                    }
                }


      
            } else {
                // 如果行未满，则继续向上检查上一行
                lastRow--;
            }
        }

        console.log("当前消除的行:", this.eliminatedRows);
        // 根据是否需要渲染的标志，决定是否重新渲染网格
        if (isRender) {
            this.musicCom.playEliminateAudio() //播放消除音效

            if (this.animcom && this.animcom1) {
                // 同时设置两个动画组件的动画播放
                this.animcom.setAnimation(0, "idle", false);
                this.animcom1.setAnimation(0, "idle", false);
                // this.animcom2.setAnimation(0, "animation1", false);
                console.log("执行动画...");
            } else {
                console.log("动画组件未全部获取到，无法同时播放动画");
            }

            // 计算得分
            const pointsPerLine = 10; // 消除一行的分数
            const score = eliminatedLines * pointsPerLine;
            //  this.gameData.score += score;
            //  console.log(`消除了 ${eliminatedLines} 行，获得 ${score} 分，当前分数：${this.gameData.score}`);
            this.addScore(score);


            //如果想要同时播放多个spine动画，则需要创建多个动画实例，让他们开始显示在不同位置
            // if (this.animcom) {
            //     const animationNode = this.animcom.node;
            //     const uiTransform = animationNode.getComponent(UITransform);
            //     if (uiTransform) {
            //         // 坐标写死部分，设置第一个坐标 (-225, 425)
            //         uiTransform.setContentSize(-225, 425);
            //         this.animcom.setAnimation(0, "idle", false);
            //         // 可以添加适当延迟，让每个坐标的动画有先后顺序播放，这里使用setTimeout模拟简单延迟，延迟时间可根据实际调整
            //         setTimeout(() => {
            //             // 坐标写死部分，设置第二个坐标 (-175, 375)
            //             uiTransform.setContentSize(-175, 375);
            //             this.animcom.setAnimation(0, "idle", false);
            //         }, 500); // 延迟500毫秒，即0.5秒，你可以根据实际情况调整这个延迟时间
            //     }
            //     console.log("执行动画...");
            // } else {
            //     console.log("动画组件未获取到，无法播放动画");
            // }
            //这里改了：先不渲染，等上面的下落动画做完再渲染！！！！！！！！！！！！！！！！！
            this.renderGrid();//就是根据gridData数组的数据0或者非0，去给gridNode数组中的预制体进行对应的贴图
        }
    }
    // uiTransform.setContentSize(canvasWidth / 2, canvasHeight / 2);

    /**
     * 向上方向，顺时针改变形状的角度
     */
    onUp() {
        this.changeShapeAngle()
    }

    /**
     * 向下加速，一次加3行
     */
    onDown() {
        this.changeShapePos(new Vec2(3, 0))
    }

    /**
     * 向左移动一列
     */
    onLeft() {
        this.changeShapePos(new Vec2(0, -1))
    }

    /**
   * 向右移动一列
   */
    onRight() {
        this.changeShapePos(new Vec2(0, 1))
    }

    /**
     * 改变形状角度，0,1,2,3  总有4种
     */
    changeShapeAngle() {
        // 先清除形状
        this.clearShape();
        // 保存改变角度前的原始形状角度索引
        let originalAngleIndex = this.shapeData.angleIndex;
        // 尝试改变形状的角度索引（按照顺时针方向改变角度）
        this.shapeData.angleIndex = this.shapeData.angleIndex === 3 ? 0 : this.shapeData.angleIndex + 1;

        if (this.checkShape()) {
            this.musicCom.playChangeAudio(); // 播放旋转音效
            // 渲染形状
            this.renderShape();
        } else {
            // 如果形状改变后不合法，恢复到原始角度索引，保持原形状不变
            this.shapeData.angleIndex = originalAngleIndex;
            console.log("此次形状角度改变无效，形状保持原样，因为改变后的形状放置不合法。");
            // 重新渲染原形状
            this.renderShape();
        }
    }

    changeShapePos(newPos: Vec2) {
        //清除形状
        this.clearShape()
        //计算形状的位置
        this.shapeData.startPos.x += newPos.x
        this.shapeData.startPos.y += newPos.y

        if (this.checkShape()) {
            // this.musicCom.playMoverAudio() //播放移动音效
            this.renderShape()
        } else {
            this.shapeData.startPos.x -= newPos.x
            this.shapeData.startPos.y -= newPos.y
            this.renderShape()
        }
    }


}